package com.spring;

import com.yxl.AppConfig;

import java.beans.Introspector;
import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Classname YxlApplicationContext
 * @Description TODO 手写Spring大致流程
 *  1、扫描ComponentScan注解，拿到注解的value值（com.yxl.service）
 * 2、将value注解值转换为com/yxl/service
 * 3、通过ClassLoader和File获取到com/yxl/service路径的绝对路径值
 * 4、如果次绝对路径值不为空的话，遍历此路径下的所有文件，获取所有文件的绝对路径
 * 5、截取绝对路径为com后.class前。例如：com\yxl\service\OrderService
 * 6、将com\yxl\service\OrderService转换为com.yxl.service.OrderService
 * 7、通过类加载器找到该类，判断该类上是否有Component注解
 * 8、判断当前类是否实现了BeanPostProcessor，并添加到beanPostProcessorList中
 * 9、如果Component没有自定义bean的名字，默认设置为类名字首字母小写
 * 10、创建BeanDefinition对象，并设置属性（此处需要判断类上是否有Scope注解，是单例还是原型）
 * 11、将BeanDefinition对象添加到beanDefinitionMap中
 * 12、循环遍历beanDefinitionMap，获取到value，判断scope为单例，执行createBean方法创建bean，并将bean放入singletonObjects单例池中
 *
 *
 * 创建bean
 * 1、根据beanDefinition的type属性拿到Class
 * 2、通过反射利用无参构造方法来实例化对象
 * 3、依赖注入：遍历此对象下的所有Field属性，判断属性上是否有Autowired注解
 * 4、进行依赖注入
 * 5、执行Aware接口
 * 6、初始化前置方法
 * 7、初始化
 * 8、初始化后置方法
 *
 *
 * 获取bean
 * 1、判断传过来的beanName在beanDefinitionMap是否存在，不存在抛出异常，存在执行下边
 * 2、通过beanName获取到BeanDefinition
 * 3、判断BeanDefinition中scope是否为单例还是原型
 * 4、如果是单例的话就去单例池查找是否存在，如果不存在就执行createBean创建
 * 5、如果是原型就执行createBean进行创建
 *
 * @Date 2023-04-01 16:06
 * @Created by yxl
 */
public class YxlApplicationContext {

    private Class configClass;

    private Map<String,BeanDefinition> beanDefinitionMap = new HashMap<>();
    private Map<String,Object> singletonObjects = new HashMap<>();// 单例池
    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

    public YxlApplicationContext(Class configClass){
        this.configClass = configClass;

        // 扫描
        scan(configClass);

        for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
            String beanName = entry.getKey();
            BeanDefinition beanDefinition = entry.getValue();
            if(beanDefinition.getScope().equals("singleton")){
                Object bean = createBean(beanName, beanDefinition);
                singletonObjects.put(beanName,bean);
            }
        }

    }

    private Object createBean(String beanName,BeanDefinition beanDefinition){
        Class clazz = beanDefinition.getType();
        Object instance = null;
        try {
            // 实例化
            instance = clazz.getConstructor().newInstance();
            // 依赖注入
            for (Field field : clazz.getDeclaredFields()) {
                boolean annotationPresent = field.isAnnotationPresent(Autowired.class);
                if(annotationPresent){
                    field.setAccessible(true);
                    field.set(instance,getBean(field.getName()));
                }
            }

            if(instance instanceof BeanNameAware){
                ((BeanNameAware)instance).setBeanName(beanName);
            }

            // 初始化前置方法
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance = beanPostProcessor.postProcessBeforeInitialization(instance,beanName);
            }

            // 初始化
            if(instance instanceof InitializingBean){
                ((InitializingBean) instance).afterPropertiesSet();
            }

            // 初始化后置方法
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance = beanPostProcessor.postProcessAfterInitialization(instance,beanName);
            }

        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return instance;
    }


    public Object getBean(String beanName){
        if(!beanDefinitionMap.containsKey(beanName)){// 如果传过来的beanname
            throw new NullPointerException();
        }

        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if(beanDefinition.getScope().equals("singleton")){
            Object singletonBean = singletonObjects.get(beanName);
            if (singletonBean==null){
                singletonBean = createBean(beanName,beanDefinition);
                singletonObjects.put(beanName,singletonBean);
            }
            return singletonBean;
        }else{
            // 原型
            Object prototypeBean = createBean(beanName, beanDefinition);
            return prototypeBean;
        }
    }

    private void scan(Class configClass){
        boolean annotationPresent = configClass.isAnnotationPresent(ComponentScan.class);
        if(annotationPresent){
            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            String path = componentScanAnnotation.value();
            path = path.replace(".", "/");
            ClassLoader classLoader = YxlApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(path);
            File file = new File(resource.getFile());
            if(file.isDirectory()){
                for (File f : file.listFiles()) {
                    String absolutePath = f.getAbsolutePath();
                    // 需要file文件加载为class对象
                    absolutePath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
                    absolutePath = absolutePath.replace("\\",".");
                    try {
                        Class<?> clazz = classLoader.loadClass(absolutePath);
                        // 判断类上是否有Component注解
                        if(clazz.isAnnotationPresent(Component.class)){

                            // 判断当前类是否实现了BeanPostProcessor
                            if(BeanPostProcessor.class.isAssignableFrom(clazz)){
                                BeanPostProcessor instance = (BeanPostProcessor) clazz.getConstructor().newInstance();
                                beanPostProcessorList.add(instance);
                            }
                            // 获取Bean的名字
                            Component annotationAnnotation = clazz.getAnnotation(Component.class);
                            String beanName = annotationAnnotation.value();
                            if("".equals(beanName)){
                                // 如果Component没有自定义bean的名字，默认设置为类名字首字母小写
                                beanName = Introspector.decapitalize(clazz.getSimpleName());
                            }

                            // 创建bean的定义
                            BeanDefinition beanDefinition = new BeanDefinition();
                            beanDefinition.setType(clazz);

                            // 判断类上是否有scope注解
                            if(clazz.isAnnotationPresent(Scope.class)){
                                Scope scope = clazz.getAnnotation(Scope.class);
                                String value = scope.value();
                                beanDefinition.setScope(value);

                            }else{
                                // 如果沒有，默认是单例的
                                beanDefinition.setScope("singleton");
                            }

                            beanDefinitionMap.put(beanName,beanDefinition);
                            // 创建Bean
                        }
                    }catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e){
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}
